package org.logstashplugins.Cache;

import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 存储正则表达式
 */
public class RegexCache{

    private final ConcurrentHashMap<String, Object> regexCache;

    private String cacheId;

    private final int CACHE_SIZE = 1024;

    private byte[] byteBuffer;


    private final AtomicInteger bufferOffset;

    private final int boom = (Integer.MAX_VALUE / 3) >> 1;

    /**
     * 用于标记该cache是否需要更新，offset会存在数据库的表中，
     * 如果查询到offset不同，则去数据库同步数据到cache中。
     *
     * 表： plugin 的字段： id, cur_plugin_offset, cur_regex_count, is_sync
     * 每次定时任务都会获取这个几个字段的值，会有下列几种情况：
     * 1、 cur_plugin_offset > cur_offset(该插件中的 {@see offset} ) :
     *     我们会将offset > cur_offset(当前插件的offset)中的正则同步到cache中
     * 2、 cur_regex_count > RegexCache.size() :
     *      此时会先查看offset是否一样，如果offset一样，说明有的数据没有同步到，此时则需要全量同步
     * 3、 is_sync 标记为是否同步，如果 is_sync 标记为 true，此时则不需要同步数据，
     *      如果is_sync 是 false 则会出发全量同步或者增量同步，并将is_sync标记位 true
     *
    */
    private final AtomicLong offset;


    public RegexCache(String cacheId){
        this.cacheId = cacheId;
        regexCache = new ConcurrentHashMap<>(CACHE_SIZE);
        byteBuffer = new byte[1024];
        offset = new AtomicLong(0);
        bufferOffset = new AtomicInteger(0);
    }


    public void putRegex(String regex){
        regexCache.put(regex, byteBuffer[ bufferOffset.getAndIncrement() ]);
        offset.getAndIncrement();
        //如果满足扩容条件，则扩容
        if(ensureByteBufferCapactiy()){
            growByteBuffer();
        }
    }

    public boolean containRegex(String regex){
        return regexCache.contains(regex);
    }

    public Set<String> getRegexSet(){
        return regexCache.keySet();
    }

    private boolean ensureByteBufferCapactiy(){
        return ((byteBuffer.length >> 2 ) * 3) <= bufferOffset.get();
    }

    private void growByteBuffer(){
        int len = byteBuffer.length;
        if(len >= boom){
            len = Integer.MAX_VALUE;
        }
        //1.5倍扩容
        len = len + (len >> 1);
        //扩容
        byteBuffer = Arrays.copyOf(byteBuffer, len);
    }

    public String getCacheId() {
        return cacheId;
    }
}
